package com.ocean.admin.service.impl;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ocean.admin.entity.SysMenu;
import com.ocean.admin.entity.SysRole;
import com.ocean.admin.mapper.SysMenuMapper;
import com.ocean.admin.model.dto.menu.SysMenuQueryDTO;
import com.ocean.admin.model.dto.menu.SysMenuSaveDTO;
import com.ocean.admin.model.dto.menu.SysMenuUpdateDTO;
import com.ocean.admin.model.vo.menu.SysMenuRuleVo;
import com.ocean.admin.model.vo.menu.SysMenuVo;
import com.ocean.admin.extension.security.core.SysUserDetails;
import com.ocean.admin.extension.security.filter.WebFilterExtend;
import com.ocean.admin.service.ISysMenuService;
import com.ocean.admin.service.ISysRoleMenuService;
import com.ocean.admin.service.ISysRoleService;
import com.ocean.core.exception.verify.ApplicationException;
import com.ocean.core.util.StringUtil;
import com.ocean.core.util.constants.ConstantsAttribute;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 菜单权限表 服务实现类
 * </p>
 *
 * @author zhang
 * @since 2022-08-16
 */
@Service
public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> implements ISysMenuService {

    @Autowired
    private ISysRoleService iSysRoleService;

    @Autowired
    private ISysRoleMenuService iSysRoleMenuService;

    /**
     * 根据用户ID查询权限
     *
     * @param userId 用户ID
     * @return 权限列表
     */
    @Override
    public Set<String> selectMenuPermsByUserId(Long userId) {
        List<String> perms = baseMapper.selectMenuPermsByUserId(userId);
        Set<String> permsSet = new HashSet<>();
        for (String perm : perms) {
            if (StringUtil.isNotNull(perm)) {
                permsSet.addAll(Arrays.asList(perm.trim().split(",")));
            }
        }
        return permsSet;
    }

    /**
     * 获取菜单列表
     *
     * @param queryDTO 查询条件
     * @return 菜单树
     */
    @Override
    public List<SysMenuVo> listTree(SysMenuQueryDTO queryDTO) {
        Long sysUserId = WebFilterExtend.getUserId();
        List<SysMenuVo> menuList = this.getMenuList(queryDTO, sysUserId);
        if (StringUtil.isNotNull(queryDTO) && StringUtil.isNotNull(queryDTO.getMenuName())) {
            return menuList.stream().filter(res -> res.getMenuName().contains(queryDTO.getMenuName()))
                    .collect(Collectors.toList());
        }
        return menuList.stream().filter(item ->
                0 == item.getParentId()
        ).peek((menu) -> menu.setChildren(getChildren(menu, menuList))).sorted(Comparator.comparingInt(menu ->
                (menu.getOrderNum() == null ? 0 : menu.getOrderNum()))).collect(Collectors.toList());
    }

    /**
     * 根据角色查询菜单
     *
     * @param roleId 角色ID
     * @return 结果
     */
    @Override
    public SysMenuRuleVo roleMenuTreeSelect(Long roleId) {
        SysMenuRuleVo menuRuleVo = new SysMenuRuleVo();
        //已存在的权限菜单
        SysRole role = iSysRoleService.getById(roleId);
        List<Long> existsMenuIdList = iSysRoleService.selectMenuListByRoleId(roleId, role.getMenuCheckStrictly());
        menuRuleVo.setExistsMenu(existsMenuIdList);
        menuRuleVo.setMenuTreeList(this.listTree(null));
        return menuRuleVo;
    }

    /**
     * 新增菜单
     *
     * @param saveDTO 菜单信息
     * @return 结果
     */
    @Override
    public String saveMenu(SysMenuSaveDTO saveDTO) {
        this.checkSaveMenu(saveDTO);
        SysMenu menu = new SysMenu();
        BeanUtils.copyProperties(saveDTO, menu);
        SysUserDetails current = WebFilterExtend.current();
        menu.setCreateUserId(current.getUserId());
        menu.setCreateUserName(current.getNickName());
        menu.setUpdateUserId(current.getUserId());
        menu.setUpdateUserName(current.getNickName());
        this.save(menu);
        return ConstantsAttribute.INSERT_SUCCESS;
    }

    /**
     * 修改菜单
     *
     * @param updateDTO 菜单信息
     * @return 结果
     */
    @Override
    public String updateMenu(SysMenuUpdateDTO updateDTO) {
        this.checkUpdateMenu(updateDTO);
        SysMenu menu = new SysMenu();
        BeanUtils.copyProperties(updateDTO, menu);
        SysUserDetails current = WebFilterExtend.current();
        menu.setUpdateUserId(current.getUserId());
        menu.setUpdateUserName(current.getNickName());
        this.updateById(menu);
        return ConstantsAttribute.UPDATE_SUCCESS;
    }

    /**
     * 删除菜单
     *
     * @param menuId 菜单ID
     * @return 结果
     */
    @Override
    public String removeMenu(Long menuId) {
        this.checkRemoveMenu(menuId);
        this.removeById(menuId);
        return ConstantsAttribute.REMOVE_SUCCESS;
    }

    private void checkSaveMenu(SysMenuSaveDTO saveDTO) {
        if (StringUtil.isNotNull(saveDTO.getParentId())) {
            List<SysMenu> parentMenus = baseMapper.selectList(Wrappers.<SysMenu>lambdaQuery().eq(SysMenu::getParentId,
                    saveDTO.getParentId()));
            for (SysMenu menu : parentMenus) {
                if (menu.getMenuName().equals(saveDTO.getMenuName())) {
                    throw new ApplicationException("菜单名已存在，请更换");
                }
            }
        }
        if (saveDTO.getIsFrame() == 1 && !StringUtil.isHttp(saveDTO.getPath())) {
            throw new ApplicationException("外链地址必须以http(s)://开头");
        }
    }

    private void checkUpdateMenu(SysMenuUpdateDTO updateDTO) {
        SysMenu menu = baseMapper.selectOne(Wrappers.<SysMenu>lambdaQuery().eq(SysMenu::getMenuName,
                updateDTO.getMenuName()).eq(SysMenu::getParentId, updateDTO.getParentId()));
        if (menu != null && !Objects.equals(menu.getMenuId(), updateDTO.getMenuId())) {
            throw new ApplicationException("菜单名已存在，请更换");
        }
        if (Objects.equals(updateDTO.getMenuId(), updateDTO.getParentId())) {
            throw new ApplicationException("上级菜单不能是自己");
        }
        if (updateDTO.getIsFrame() == 1 && !StringUtil.isHttp(updateDTO.getPath())) {
            throw new ApplicationException("外链地址必须以http(s)://开头");
        }
    }

    private void checkRemoveMenu(Long menuId) {
        Long count = baseMapper.selectCount(Wrappers.<SysMenu>lambdaQuery()
                .eq(SysMenu::getParentId, menuId));
        if (count > 0) {
            throw new ApplicationException("改菜单还存在子菜单,不能删除");
        }
        if (iSysRoleMenuService.checkMenuExistRole(menuId)) {
            throw new ApplicationException("菜单已被分配到角色，不能删除");
        }
    }

    private List<SysMenuVo> getChildren(SysMenuVo root, List<SysMenuVo> all) {
        return all.stream().filter(item -> item.getParentId().equals(root.getMenuId()))
                .peek(item -> item.setChildren(getChildren(item, all)))
                .sorted(Comparator.comparingInt(menu -> (menu.getOrderNum() == null ? 0 : menu.getOrderNum())))
                .collect(Collectors.toList());
    }

    /**
     * 获取所有菜单
     *
     * @param queryDTO  查询条件
     * @param sysUserId 用户ID
     * @return 结果
     */
    private List<SysMenuVo> getMenuList(SysMenuQueryDTO queryDTO, Long sysUserId) {
        List<SysMenuVo> menuList;
        if (WebFilterExtend.hasAdmin()) {
            menuList = baseMapper.selectMenuAllList(queryDTO);
        } else {
            menuList = baseMapper.selectMenuList(queryDTO, sysUserId);
        }
        return menuList;
    }
}
